Validation + callback api
This commit is contained in:
@@ -8,12 +8,23 @@
|
||||
// - Single-writer semantics: Each metric instance bound to creating thread
|
||||
// - Lock-free operations using atomic<uint64_t> storage for doubles
|
||||
// - Full IEEE 754 double precision preservation via bit reinterpretation
|
||||
// - Single global registry: All metrics registered in one global namespace
|
||||
//
|
||||
// CRITICAL THREAD SAFETY CONSTRAINT:
|
||||
// Each metric instance has exactly ONE writer thread (the creating thread).
|
||||
// It is undefined behavior to call inc()/dec()/set()/observe() from a different
|
||||
// thread.
|
||||
//
|
||||
// REGISTRY MODEL:
|
||||
// This implementation uses a single global registry for all metrics, unlike
|
||||
// typical Prometheus client libraries that support multiple registries.
|
||||
// This design choice prioritizes simplicity and performance over flexibility.
|
||||
//
|
||||
// METRIC LIFECYCLE:
|
||||
// Metrics are created once and persist for the application lifetime. There is
|
||||
// no unregistration mechanism - this prevents accidental metric loss and
|
||||
// simplifies the implementation.
|
||||
//
|
||||
// USAGE:
|
||||
// auto counter_family = metric::create_counter("requests_total", "Total
|
||||
// requests"); auto counter = counter_family.create({{"method", "GET"}}); //
|
||||
@@ -100,6 +111,8 @@ template <class T> struct Family {
|
||||
|
||||
// Create metric instance with specific labels
|
||||
// Labels are sorted by key for Prometheus compatibility
|
||||
// ERROR: Will abort if labels already registered via register_callback()
|
||||
// OK: Multiple calls with same labels return same instance (idempotent)
|
||||
T create(std::vector<std::pair<std::string, std::string>> labels);
|
||||
|
||||
private:
|
||||
@@ -129,6 +142,42 @@ Family<Histogram> create_histogram(std::string name, std::string help,
|
||||
std::initializer_list<double> buckets);
|
||||
|
||||
// Render all metrics in Prometheus text format
|
||||
std::span<std::string> render(ArenaAllocator &arena);
|
||||
// TODO: Implement Prometheus text exposition format
|
||||
// THREAD SAFETY: Serialized by global mutex - callbacks need not be thread-safe
|
||||
std::span<std::string_view> render(ArenaAllocator &arena);
|
||||
|
||||
// Validation functions for Prometheus compatibility
|
||||
bool is_valid_metric_name(const std::string &name);
|
||||
bool is_valid_label_key(const std::string &key);
|
||||
bool is_valid_label_value(const std::string &value);
|
||||
|
||||
// Callback function type for dynamic metric values
|
||||
// Called during render() to get current metric value
|
||||
// THREAD SAFETY: May be called from arbitrary thread, but serialized by
|
||||
// render() mutex
|
||||
// - no need to be thread-safe internally
|
||||
template <typename T> using MetricCallback = std::function<double()>;
|
||||
|
||||
// Register callback-based metric to Family
|
||||
// Validates that label set isn't already taken by either:
|
||||
// - A previous register_callback() call (callbacks must be unique)
|
||||
// - A create() call (static and callback metrics cannot coexist for same
|
||||
// labels)
|
||||
//
|
||||
// Similarly, create() will validate that label set isn't already registered as
|
||||
// callback Note: create() can be called multiple times with same labels
|
||||
// (returns same instance)
|
||||
template <>
|
||||
void Family<Counter>::register_callback(
|
||||
std::vector<std::pair<std::string, std::string>> labels,
|
||||
MetricCallback<Counter> callback);
|
||||
|
||||
template <>
|
||||
void Family<Gauge>::register_callback(
|
||||
std::vector<std::pair<std::string, std::string>> labels,
|
||||
MetricCallback<Gauge> callback);
|
||||
|
||||
// Note: Histograms do not support callbacks due to their multi-value nature
|
||||
// (buckets + sum + count). Use static histogram metrics only.
|
||||
|
||||
} // namespace metric
|
||||
|
||||
Reference in New Issue
Block a user