proxy_sdk/
metrics.rs

1use std::{cell::RefCell, collections::HashMap};
2
3use crate::{
4    dispatcher::root_id,
5    hostcalls::{self, MetricType},
6    log_concern, Status,
7};
8
9#[derive(Default)]
10pub struct MetricsInfo {
11    counters: HashMap<String, u32>,
12    gauges: HashMap<String, u32>,
13    histograms: HashMap<String, u32>,
14}
15
16thread_local! {
17    static METRICS: RefCell<HashMap<u32, MetricsInfo>> = RefCell::default();
18}
19
20/// Envoy counter metric handle
21#[derive(Clone, Copy, Debug)]
22pub struct Counter(u32);
23
24/// Const wrapper for [`Counter`]
25pub struct ConstCounter {
26    name: &'static str,
27}
28
29impl ConstCounter {
30    /// Const wrapper for [`Counter::define`]
31    pub const fn define(name: &'static str) -> Self {
32        Self { name }
33    }
34
35    pub fn get(&self) -> Counter {
36        Counter::define(self.name)
37    }
38}
39
40impl Counter {
41    /// Defines a new counter, reusing an old handle if it already exists. It is safe to call this multiple times with the same name.
42    pub fn define(name: impl AsRef<str>) -> Self {
43        METRICS.with_borrow_mut(|metrics| {
44            let metrics = metrics.entry(root_id()).or_default();
45            if let Some(counter) = metrics.counters.get(name.as_ref()) {
46                return Self(*counter);
47            }
48            let out = log_concern(
49                "define-metric",
50                hostcalls::define_metric(MetricType::Counter, name.as_ref()),
51            );
52            metrics.counters.insert(name.as_ref().to_string(), out);
53            Self(out)
54        })
55    }
56
57    /// Retrieves the current metric value
58    pub fn get(&self) -> Result<u64, Status> {
59        hostcalls::get_metric(self.0)
60    }
61
62    /// Records an absolute count of this metric
63    pub fn record(&self, value: u64) {
64        log_concern("record-metric", hostcalls::record_metric(self.0, value));
65    }
66
67    /// Increments the count of this metric by `offset`
68    pub fn increment(&self, offset: i64) {
69        log_concern(
70            "increment-metric",
71            hostcalls::increment_metric(self.0, offset),
72        );
73    }
74}
75
76/// Envoy gauge metric handle
77#[derive(Clone, Copy, Debug)]
78pub struct Gauge(u32);
79
80/// Const wrapper for [`Gauge`]
81pub struct ConstGauge {
82    name: &'static str,
83}
84
85impl ConstGauge {
86    /// Const wrapper for [`Gauge::define`]
87    pub const fn define(name: &'static str) -> Self {
88        Self { name }
89    }
90
91    pub fn get(&self) -> Gauge {
92        Gauge::define(self.name)
93    }
94}
95
96impl Gauge {
97    /// Defines a new gauge, reusing an old handle if it already exists. It is safe to call this multiple times with the same name.
98    pub fn define(name: impl AsRef<str>) -> Self {
99        METRICS.with_borrow_mut(|metrics| {
100            let metrics = metrics.entry(root_id()).or_default();
101            if let Some(gauge) = metrics.gauges.get(name.as_ref()) {
102                return Self(*gauge);
103            }
104            let out = log_concern(
105                "define-metric",
106                hostcalls::define_metric(MetricType::Gauge, name.as_ref()),
107            );
108            metrics.gauges.insert(name.as_ref().to_string(), out);
109            Self(out)
110        })
111    }
112
113    /// Retrieves the current metric value
114    pub fn get(&self) -> Result<u64, Status> {
115        hostcalls::get_metric(self.0)
116    }
117
118    /// Records an absolute count of this metric
119    pub fn record(&self, value: u64) {
120        log_concern("record-metric", hostcalls::record_metric(self.0, value));
121    }
122
123    /// Increments the count of this metric by `offset`
124    pub fn increment(&self, offset: i64) {
125        log_concern(
126            "increment-metric",
127            hostcalls::increment_metric(self.0, offset),
128        );
129    }
130}
131
132/// Envoy histogram metric handle
133#[derive(Clone, Copy, Debug)]
134pub struct Histogram(u32);
135
136/// Const wrapper for [`Histogram`]
137pub struct ConstHistogram {
138    name: &'static str,
139}
140
141impl ConstHistogram {
142    /// Const wrapper for [`Histogram::define`]
143    pub const fn define(name: &'static str) -> Self {
144        Self { name }
145    }
146
147    pub fn get(&self) -> Histogram {
148        Histogram::define(self.name)
149    }
150}
151
152impl Histogram {
153    /// Defines a new histogram, reusing an old handle if it already exists. It is safe to call this multiple times with the same name.
154    pub fn define(name: impl AsRef<str>) -> Self {
155        METRICS.with_borrow_mut(|metrics| {
156            let metrics = metrics.entry(root_id()).or_default();
157            if let Some(histogram) = metrics.histograms.get(name.as_ref()) {
158                return Self(*histogram);
159            }
160            let out = log_concern(
161                "define-metric",
162                hostcalls::define_metric(MetricType::Histogram, name.as_ref()),
163            );
164            metrics.histograms.insert(name.as_ref().to_string(), out);
165            Self(out)
166        })
167    }
168
169    /// Records a new item for this histogram
170    pub fn record(&self, value: u64) {
171        log_concern("record-metric", hostcalls::record_metric(self.0, value));
172    }
173}