Flesh out metrics architecture more
This commit is contained in:
@@ -1,64 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
// WeaselDB Metrics System
|
||||
//
|
||||
// High-performance metrics collection with Prometheus-compatible output.
|
||||
//
|
||||
// DESIGN PRINCIPLES:
|
||||
// - 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
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// USAGE:
|
||||
// auto counter_family = metric::create_counter("requests_total", "Total
|
||||
// requests"); auto counter = counter_family.create({{"method", "GET"}}); //
|
||||
// Bound to this thread counter.inc(1.0); // ONLY call from creating thread
|
||||
//
|
||||
// auto histogram_family = metric::create_histogram("latency", "Request
|
||||
// latency", {0.1, 0.5, 1.0}); auto histogram =
|
||||
// histogram_family.create({{"endpoint", "/api"}}); // Bound to this thread
|
||||
// histogram.observe(0.25); // ONLY call from creating thread
|
||||
|
||||
#include "arena_allocator.hpp"
|
||||
#include <initializer_list>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace metric {
|
||||
|
||||
// Counter: Monotonically increasing metric with single-writer semantics
|
||||
// Use for: request counts, error counts, bytes processed, etc.
|
||||
//
|
||||
// THREAD SAFETY: Each counter instance has exactly ONE writer thread (the one
|
||||
// that created it). It is an error to call inc() from any thread other than the
|
||||
// creating thread. Multiple readers can safely read the value from other
|
||||
// threads.
|
||||
struct Counter {
|
||||
void inc(double = 1.0);
|
||||
void
|
||||
inc(double = 1.0); // Increment counter (must be >= 0) - SINGLE WRITER ONLY
|
||||
|
||||
private:
|
||||
Counter();
|
||||
friend struct Metric;
|
||||
template <class> friend struct Family;
|
||||
struct State;
|
||||
State *p;
|
||||
};
|
||||
|
||||
// Gauge: Can increase/decrease metric
|
||||
// Use for: memory usage, active connections, queue depth, etc.
|
||||
//
|
||||
// THREAD SAFETY: Each gauge instance has exactly ONE writer thread (the one
|
||||
// that created it). It is an error to call inc()/dec()/set() from any thread
|
||||
// other than the creating thread.
|
||||
struct Gauge {
|
||||
void inc(double = 1.0);
|
||||
void dec(double = 1.0);
|
||||
void set(double);
|
||||
void inc(double = 1.0); // Increase gauge value - SINGLE WRITER ONLY
|
||||
void dec(double = 1.0); // Decrease gauge value - SINGLE WRITER ONLY
|
||||
void set(double); // Set absolute value - SINGLE WRITER ONLY
|
||||
|
||||
private:
|
||||
Gauge();
|
||||
friend struct Metric;
|
||||
template <class> friend struct Family;
|
||||
struct State;
|
||||
State *p;
|
||||
};
|
||||
|
||||
// Histogram: Distribution tracking with single-writer semantics
|
||||
// Use for: request latency, response size, processing time, etc.
|
||||
// Buckets are automatically sorted, deduplicated, and include +Inf
|
||||
//
|
||||
// THREAD SAFETY: Each histogram instance has exactly ONE writer thread (the one
|
||||
// that created it). It is an error to call observe() from any thread other than
|
||||
// the creating thread. Multiple readers can safely read bucket values from
|
||||
// other threads.
|
||||
struct Histogram {
|
||||
void observe(double);
|
||||
void observe(
|
||||
double); // Record observation in appropriate bucket - SINGLE WRITER ONLY
|
||||
|
||||
private:
|
||||
Histogram();
|
||||
friend struct Metric;
|
||||
template <class> friend struct Family;
|
||||
struct State;
|
||||
State *p;
|
||||
};
|
||||
|
||||
// Family: Factory for creating metric instances with different label
|
||||
// combinations Each family represents one metric name with varying labels
|
||||
template <class T> struct Family {
|
||||
static_assert(std::is_same_v<T, Counter> || std::is_same_v<T, Gauge> ||
|
||||
std::is_same_v<T, Histogram>);
|
||||
T create(std::vector<std::pair<std::string, std::string>>);
|
||||
|
||||
// Create metric instance with specific labels
|
||||
// Labels are sorted by key for Prometheus compatibility
|
||||
T create(std::vector<std::pair<std::string, std::string>> labels);
|
||||
|
||||
private:
|
||||
Family();
|
||||
friend struct Metric;
|
||||
std::string_view name;
|
||||
std::string_view help;
|
||||
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>);
|
||||
|
||||
struct State;
|
||||
State *p;
|
||||
};
|
||||
|
||||
// All std::string_view's should point to static memory
|
||||
Family<Counter> create_counter(std::string_view name, std::string_view help);
|
||||
Family<Gauge> create_gauge(std::string_view name, std::string_view help);
|
||||
Family<Histogram> create_histogram(std::string_view name, std::string_view help,
|
||||
// Factory functions for creating metric families
|
||||
// IMPORTANT: name and help must point to static memory (string literals)
|
||||
|
||||
// Create counter family (monotonically increasing values)
|
||||
Family<Counter> create_counter(std::string name, std::string help);
|
||||
|
||||
// Create gauge family (can increase/decrease)
|
||||
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
|
||||
Family<Histogram> create_histogram(std::string name, std::string help,
|
||||
std::initializer_list<double> buckets);
|
||||
|
||||
std::span<std::string_view> render(ArenaAllocator &);
|
||||
// Render all metrics in Prometheus text format
|
||||
std::span<std::string> render(ArenaAllocator &arena);
|
||||
|
||||
} // namespace metric
|
||||
|
||||
Reference in New Issue
Block a user