From 52fbf8ce5df9d1e23f9a8eb452ab3ba4617e4dad Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Mon, 26 Dec 2022 18:17:27 +0100 Subject: [PATCH] feat: added gauge type & some tests --- .gitignore | 2 + collector.v | 103 +++++++++++++++++++++++++++----------- collector_test.v | 112 ++++++++++++++++++++++++++++++++++++++++++ counter_test.v | 14 ------ exporter/exporter.v | 10 ++++ exporter/prometheus.v | 4 ++ metrics.v | 14 ++++-- null.v | 18 ++++++- 8 files changed, 227 insertions(+), 50 deletions(-) create mode 100644 .gitignore create mode 100644 collector_test.v delete mode 100644 counter_test.v create mode 100644 exporter/exporter.v create mode 100644 exporter/prometheus.v diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b8a745 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.c +*.so diff --git a/collector.v b/collector.v index 2dde2d8..ca2ea2a 100644 --- a/collector.v +++ b/collector.v @@ -2,12 +2,12 @@ module metrics import sync.stdatomic -struct CounterEntry { +struct Counter { metric Metric - index int + data u64 } -struct HistogramEntry { +struct FloatSeries { metric Metric pub mut: data shared []f64 @@ -16,72 +16,66 @@ pub mut: [heap] struct DefaultCollector { mut: - // The mutex for counters also applies to counter_indexes. Both data - // structuress are only ever updated together. Note that only registering a - // new metric actually changes these; all operations on already existing - // metrics use atomic operations on the elements of the counters array. - counters shared []u64 - counter_indexes map[string]CounterEntry - histograms shared map[string]HistogramEntry + counters shared map[string]&Counter + histograms shared map[string]&FloatSeries + gauges shared map[string]&FloatSeries } pub fn new_default_collector() &DefaultCollector { return &DefaultCollector{ - counters: []u64{} - counter_indexes: map[string]CounterEntry{} - histograms: map[string]HistogramEntry{} + counters: map[string]&Counter{} + histograms: map[string]&FloatSeries{} + gauges: map[string]&FloatSeries{} } } pub fn (mut c DefaultCollector) counter_register(value u64, metric Metric) { lock c.counters { - c.counters << value - - c.counter_indexes[metric.str()] = CounterEntry{ + c.counters[metric.str()] = &Counter{ metric: metric - index: c.counters.len - 1 + data: value } } } pub fn (c &DefaultCollector) counter_increment(metric Metric) { rlock c.counters { - entry := c.counter_indexes[metric.str()] + entry := c.counters[metric.str()] - stdatomic.add_u64(&c.counters[entry.index], 1) + stdatomic.add_u64(&entry.data, 1) } } pub fn (c &DefaultCollector) counter_get(metric Metric) ?u64 { return rlock c.counters { - entry := c.counter_indexes[metric.str()] or { return none } + entry := c.counters[metric.str()] or { return none } - stdatomic.load_u64(&c.counters[entry.index]) + stdatomic.load_u64(&entry.data) } } pub fn (c &DefaultCollector) counters() []Metric { - mut metrics := []Metric{} + mut metrics := []Metric{} - rlock c.counters { - for _, entry in c.counter_indexes { - metrics << entry.metric - } - } + rlock c.counters { + for _, entry in c.counters { + metrics << entry.metric + } + } - return metrics + return metrics } pub fn (mut c DefaultCollector) histogram_register(metric Metric) { lock c.histograms { - c.histograms[metric.str()] = HistogramEntry{ + c.histograms[metric.str()] = &FloatSeries{ metric: metric data: []f64{} } } } -pub fn (c &DefaultCollector) histogram_add(value f64, metric Metric) { +pub fn (c &DefaultCollector) histogram_record(value f64, metric Metric) { entry := rlock c.histograms { c.histograms[metric.str()] } @@ -102,3 +96,52 @@ pub fn (c &DefaultCollector) histogram_get(metric Metric) ?[]f64 { entry.data.clone() } } + +pub fn (mut c DefaultCollector) gauge_register(value f64, metric Metric) { + lock c.gauges { + c.gauges[metric.str()] = &FloatSeries{ + metric: metric + data: [value] + } + } +} + +pub fn (c &DefaultCollector) gauge_add(value f64, metric Metric) { + entry := rlock c.gauges { + c.gauges[metric.str()] + } + + lock entry.data { + entry.data[0] += value + } +} + +pub fn (c &DefaultCollector) gauge_sub(value f64, metric Metric) { + entry := rlock c.gauges { + c.gauges[metric.str()] + } + + lock entry.data { + entry.data[0] -= value + } +} + +pub fn (c &DefaultCollector) gauge_set(value f64, metric Metric) { + entry := rlock c.gauges { + c.gauges[metric.str()] + } + + lock entry.data { + entry.data[0] = value + } +} + +pub fn (c &DefaultCollector) gauge_get(metric Metric) ?f64 { + entry := rlock c.gauges { + c.gauges[metric.str()] + } + + return rlock entry.data { + entry.data[0] + } +} diff --git a/collector_test.v b/collector_test.v new file mode 100644 index 0000000..fd79b4b --- /dev/null +++ b/collector_test.v @@ -0,0 +1,112 @@ +module metrics + +fn test_implements_interface() { + _ := MetricsCollector(new_default_collector()) +} + +fn test_null_implements_interface() { + _ := MetricsCollector(new_null_collector()) +} + +fn test_counter_increment() { + mut m := new_default_collector() + + m.counter_register(0, name: 'test') + m.counter_increment(name: 'test') + assert m.counter_get(name: 'test')? == u64(1) + + m.counter_increment(name: 'test') + assert m.counter_get(name: 'test')? == u64(2) + + // Test with labels + metric := Metric{ + name: 'test2' + labels: [['hi', 'label']!, ['hi2', 'label2']!] + } + + m.counter_register(15, metric) + m.counter_increment(metric) + assert m.counter_get(metric)? == u64(16) +} + +fn test_histogram() { + mut m := new_default_collector() + + m.histogram_register(name: 'test') + m.histogram_record(5.0, name: 'test') + assert m.histogram_get(name: 'test')? == [5.0] + + m.histogram_record(7.0, name: 'test') + assert m.histogram_get(name: 'test')? == [5.0, 7.0] + + // Test with labels + metric := Metric{ + name: 'test2' + labels: [['hi', 'label']!, ['hi2', 'label2']!] + } + + m.histogram_register(metric) + m.histogram_record(5.0, metric) + assert m.histogram_get(metric)? == [5.0] + + m.histogram_record(7.0, metric) + assert m.histogram_get(metric)? == [5.0, 7.0] +} + +fn test_gauge_add() { + mut m := new_default_collector() + + m.gauge_register(0.0, name: 'test') + m.gauge_add(5.0, name: 'test') + assert m.gauge_get(name: 'test')? == 5.0 + + // Test with labels + metric := Metric{ + name: 'test2' + labels: [['hi', 'label']!, ['hi2', 'label2']!] + } + + m.gauge_register(3.0, metric) + m.gauge_add(5.0, metric) + assert m.gauge_get(metric)? == 8.0 +} + +fn test_gauge_sub() { + mut m := new_default_collector() + + m.gauge_register(0.0, name: 'test') + m.gauge_sub(5.0, name: 'test') + assert m.gauge_get(name: 'test')? == -5.0 + + // Test with labels + metric := Metric{ + name: 'test2' + labels: [['hi', 'label']!, ['hi2', 'label2']!] + } + + m.gauge_register(3.0, metric) + m.gauge_sub(5.0, metric) + assert m.gauge_get(metric)? == -2.0 +} + +fn test_gauge_set() { + mut m := new_default_collector() + + m.gauge_register(0.0, name: 'test') + m.gauge_set(3.0, name: 'test') + assert m.gauge_get(name: 'test')? == 3.0 + m.gauge_set(5.0, name: 'test') + assert m.gauge_get(name: 'test')? == 5.0 + + // Test with labels + metric := Metric{ + name: 'test2' + labels: [['hi', 'label']!, ['hi2', 'label2']!] + } + + m.gauge_register(0.0, metric) + m.gauge_set(3.0, metric) + assert m.gauge_get(metric)? == 3.0 + m.gauge_set(5.0, metric) + assert m.gauge_get(metric)? == 5.0 +} diff --git a/counter_test.v b/counter_test.v deleted file mode 100644 index cbbaf48..0000000 --- a/counter_test.v +++ /dev/null @@ -1,14 +0,0 @@ -module metrics - -fn test_counter_increment() { - mut m := new_default_collector() - m.counter_register(0, name: 'test') - - m.counter_increment(name: 'test') - - assert m.counter_get(name: 'test')? == u64(1) - - m.counter_increment(name: 'test') - - assert m.counter_get(name: 'test')? == u64(2) -} diff --git a/exporter/exporter.v b/exporter/exporter.v new file mode 100644 index 0000000..c0f4f3f --- /dev/null +++ b/exporter/exporter.v @@ -0,0 +1,10 @@ +module exporter + +import io +import metrics { MetricsCollector } + +pub interface MetricsExporter { + load(collector MetricsCollector) + export_to_writer(writer io.Writer) ! + export_to_string() string ! +} diff --git a/exporter/prometheus.v b/exporter/prometheus.v new file mode 100644 index 0000000..77af204 --- /dev/null +++ b/exporter/prometheus.v @@ -0,0 +1,4 @@ +module exporter + +pub struct PrometheusExporter { +} diff --git a/metrics.v b/metrics.v index 1639f6f..9ba93d8 100644 --- a/metrics.v +++ b/metrics.v @@ -20,11 +20,17 @@ pub fn (m &Metric) str() string { } pub interface MetricsCollector { - counter_register(value u64, metric Metric) counter_increment(metric Metric) counter_get(metric Metric) ?u64 - counters() []Metric + counters() []Metric + histogram_record(value f64, metric Metric) + histogram_get(metric Metric) ?[]f64 + gauge_add(value f64, metric Metric) + gauge_sub(value f64, metric Metric) + gauge_set(value f64, metric Metric) + gauge_get(metric Metric) ?f64 +mut: + counter_register(value u64, metric Metric) histogram_register(metric Metric) - histogram_add(value f64, metric Metric) - histogram_get(metric Metric) ? + gauge_register(value f64, metric Metric) } diff --git a/null.v b/null.v index 19275e4..24fd490 100644 --- a/null.v +++ b/null.v @@ -15,12 +15,26 @@ pub fn (c &NullCollector) counter_get(metric Metric) ?u64 { return none } -pub fn (c &NullCollector) counters() []Metric { return [] } +pub fn (c &NullCollector) counters() []Metric { + return [] +} pub fn (c &NullCollector) histogram_register(metric Metric) {} -pub fn (c &NullCollector) histogram_add(value f64, metric Metric) {} +pub fn (c &NullCollector) histogram_record(value f64, metric Metric) {} pub fn (c &NullCollector) histogram_get(metric Metric) ?[]f64 { return none } + +pub fn (c &NullCollector) gauge_register(value f64, metric Metric) {} + +pub fn (c &NullCollector) gauge_add(value f64, metric Metric) {} + +pub fn (c &NullCollector) gauge_sub(value f64, metric Metric) {} + +pub fn (c &NullCollector) gauge_set(value f64, metric Metric) {} + +pub fn (c &NullCollector) gauge_get(metric Metric) ?f64 { + return none +}