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#[derive(Clone, Copy, Debug)]
22pub struct Counter(u32);
23
24pub struct ConstCounter {
26 name: &'static str,
27}
28
29impl ConstCounter {
30 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 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 pub fn get(&self) -> Result<u64, Status> {
59 hostcalls::get_metric(self.0)
60 }
61
62 pub fn record(&self, value: u64) {
64 log_concern("record-metric", hostcalls::record_metric(self.0, value));
65 }
66
67 pub fn increment(&self, offset: i64) {
69 log_concern(
70 "increment-metric",
71 hostcalls::increment_metric(self.0, offset),
72 );
73 }
74}
75
76#[derive(Clone, Copy, Debug)]
78pub struct Gauge(u32);
79
80pub struct ConstGauge {
82 name: &'static str,
83}
84
85impl ConstGauge {
86 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 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 pub fn get(&self) -> Result<u64, Status> {
115 hostcalls::get_metric(self.0)
116 }
117
118 pub fn record(&self, value: u64) {
120 log_concern("record-metric", hostcalls::record_metric(self.0, value));
121 }
122
123 pub fn increment(&self, offset: i64) {
125 log_concern(
126 "increment-metric",
127 hostcalls::increment_metric(self.0, offset),
128 );
129 }
130}
131
132#[derive(Clone, Copy, Debug)]
134pub struct Histogram(u32);
135
136pub struct ConstHistogram {
138 name: &'static str,
139}
140
141impl ConstHistogram {
142 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 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 pub fn record(&self, value: u64) {
171 log_concern("record-metric", hostcalls::record_metric(self.0, value));
172 }
173}