Metrics implementation, WIP

This commit is contained in:
2025-08-29 13:43:03 -04:00
parent fac0d20ae1
commit 62b37c067c
5 changed files with 1270 additions and 61 deletions

View File

@@ -35,15 +35,26 @@
// histogram_family.create({{"endpoint", "/api"}}); // Bound to this thread
// histogram.observe(0.25); // ONLY call from creating thread
#include "arena_allocator.hpp"
#include <functional>
#include <initializer_list>
#include <span>
#include <string>
#include <type_traits>
#include <vector>
#include "arena_allocator.hpp"
namespace metric {
// Forward declarations
template <typename T> struct Family;
// 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()>;
// Counter: Monotonically increasing metric with single-writer semantics
// Use for: request counts, error counts, bytes processed, etc.
//
@@ -115,13 +126,19 @@ template <class T> struct Family {
// OK: Multiple calls with same labels return same instance (idempotent)
T create(std::vector<std::pair<std::string, std::string>> labels);
// Register callback-based metric (Counter and Gauge only)
// Validates that label set isn't already taken
void
register_callback(std::vector<std::pair<std::string, std::string>> labels,
MetricCallback<T> callback);
private:
Family();
friend struct Metric;
friend Family<Counter> create_counter(std::string, std::string);
friend Family<Gauge> create_gauge(std::string, std::string);
friend Family<Histogram> create_histogram(std::string, std::string,
std::initializer_list<double>);
std::span<const double>);
struct State;
State *p;
@@ -131,15 +148,33 @@ private:
// IMPORTANT: name and help must point to static memory (string literals)
// Create counter family (monotonically increasing values)
// ERROR: Aborts if family with same name is registered with different help
// text.
Family<Counter> create_counter(std::string name, std::string help);
// Create gauge family (can increase/decrease)
// ERROR: Aborts if family with same name is registered with different help
// text.
Family<Gauge> create_gauge(std::string name, std::string help);
// Create histogram family with custom buckets
// Buckets will be sorted, deduplicated, and +Inf will be added automatically
// ERROR: Aborts if family with same name is registered with different help text
// or buckets.
Family<Histogram> create_histogram(std::string name, std::string help,
std::initializer_list<double> buckets);
std::span<const double> buckets);
// Helper functions for generating standard histogram buckets
// Following Prometheus client library conventions
// Generate linear buckets: start, start+width, start+2*width, ...,
// start+(count-1)*width Example: linear_buckets(0, 10, 5) = {0, 10, 20, 30, 40}
std::vector<double> linear_buckets(double start, double width, int count);
// Generate exponential buckets: start, start*factor, start*factor^2, ...,
// start*factor^(count-1) Example: exponential_buckets(1, 2, 5) = {1, 2, 4, 8,
// 16}
std::vector<double> exponential_buckets(double start, double factor, int count);
// Render all metrics in Prometheus text format
// Returns chunks of Prometheus exposition format (includes # HELP and # TYPE
@@ -155,33 +190,7 @@ 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
} // namespace metric