saluki_core/data_model/event/metric/
mod.rs1pub mod context;
4use self::context::Context;
5
6mod metadata;
7use std::time::Duration;
8
9pub use self::metadata::*;
10
11mod value;
12pub use self::value::{
13 Histogram, HistogramPoints, HistogramSummary, MetricValues, ScalarPoints, SetPoints, SketchPoints,
14};
15
16#[derive(Clone, Debug, PartialEq)]
37pub struct Metric {
38 context: Context,
39 values: MetricValues,
40 metadata: MetricMetadata,
41}
42
43impl Metric {
44 pub fn counter<C, V>(context: C, values: V) -> Self
48 where
49 C: Into<Context>,
50 V: Into<ScalarPoints>,
51 {
52 Self {
53 context: context.into(),
54 values: MetricValues::counter(values),
55 metadata: MetricMetadata::default(),
56 }
57 }
58
59 pub fn gauge<C, V>(context: C, values: V) -> Self
63 where
64 C: Into<Context>,
65 V: Into<ScalarPoints>,
66 {
67 Self {
68 context: context.into(),
69 values: MetricValues::gauge(values),
70 metadata: MetricMetadata::default(),
71 }
72 }
73
74 pub fn rate<C, V>(context: C, values: V, interval: Duration) -> Self
78 where
79 C: Into<Context>,
80 V: Into<ScalarPoints>,
81 {
82 Self {
83 context: context.into(),
84 values: MetricValues::rate(values, interval),
85 metadata: MetricMetadata::default(),
86 }
87 }
88
89 pub fn set<C, V>(context: C, values: V) -> Self
93 where
94 C: Into<Context>,
95 V: Into<SetPoints>,
96 {
97 Self {
98 context: context.into(),
99 values: MetricValues::set(values),
100 metadata: MetricMetadata::default(),
101 }
102 }
103
104 pub fn histogram<C, V>(context: C, values: V) -> Self
108 where
109 C: Into<Context>,
110 V: Into<HistogramPoints>,
111 {
112 Self {
113 context: context.into(),
114 values: MetricValues::histogram(values),
115 metadata: MetricMetadata::default(),
116 }
117 }
118
119 pub fn distribution<C, V>(context: C, values: V) -> Self
123 where
124 C: Into<Context>,
125 V: Into<SketchPoints>,
126 {
127 Self {
128 context: context.into(),
129 values: MetricValues::distribution(values),
130 metadata: MetricMetadata::default(),
131 }
132 }
133
134 pub fn context(&self) -> &Context {
136 &self.context
137 }
138
139 pub fn context_mut(&mut self) -> &mut Context {
141 &mut self.context
142 }
143
144 pub fn values(&self) -> &MetricValues {
146 &self.values
147 }
148
149 pub fn values_mut(&mut self) -> &mut MetricValues {
151 &mut self.values
152 }
153
154 pub fn metadata(&self) -> &MetricMetadata {
156 &self.metadata
157 }
158
159 pub fn metadata_mut(&mut self) -> &mut MetricMetadata {
161 &mut self.metadata
162 }
163
164 pub fn into_parts(self) -> (Context, MetricValues, MetricMetadata) {
166 (self.context, self.values, self.metadata)
167 }
168
169 pub fn from_parts(context: Context, values: MetricValues, metadata: MetricMetadata) -> Self {
171 Self {
172 context,
173 values,
174 metadata,
175 }
176 }
177}
178
179#[derive(Clone, Copy)]
186pub struct SampleRate(f64);
187
188impl SampleRate {
189 pub const fn unsampled() -> Self {
191 Self(1.0)
192 }
193
194 pub const fn rate(&self) -> f64 {
196 self.0
197 }
198
199 pub fn weight(&self) -> u64 {
201 (1.0 / self.0) as u64
202 }
203
204 pub fn raw_weight(&self) -> f64 {
206 1.0 / self.0
207 }
208}
209
210impl TryFrom<f64> for SampleRate {
211 type Error = &'static str;
212
213 fn try_from(value: f64) -> Result<Self, Self::Error> {
214 if !(0.0..=1.0).contains(&value) {
215 Err("sample rate must be between 0.0 and 1.0")
216 } else {
217 Ok(Self(value))
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::SampleRate;
225
226 #[test]
227 fn try_from_accepts_the_inclusive_unit_interval() {
228 assert!(SampleRate::try_from(0.0).is_ok());
230 assert!(SampleRate::try_from(0.5).is_ok());
231 assert!(SampleRate::try_from(1.0).is_ok());
232 }
233
234 #[test]
235 fn try_from_rejects_values_outside_the_unit_interval() {
236 assert!(SampleRate::try_from(-0.1).is_err());
237 assert!(SampleRate::try_from(1.1).is_err());
238 }
239
240 #[test]
241 fn weight_is_the_reciprocal_of_the_rate() {
242 let rate = SampleRate::try_from(0.25).unwrap();
244 assert_eq!(rate.rate(), 0.25);
245 assert_eq!(rate.weight(), 4);
246 assert_eq!(rate.raw_weight(), 4.0);
247 }
248
249 #[test]
250 fn integer_weight_truncates_non_integer_reciprocals() {
251 let rate = SampleRate::try_from(0.3).unwrap();
253 assert_eq!(rate.weight(), 3);
254 assert!((rate.raw_weight() - (1.0 / 0.3)).abs() < f64::EPSILON);
255 }
256
257 #[test]
258 fn unsampled_has_unit_rate_and_weight() {
259 let rate = SampleRate::unsampled();
260 assert_eq!(rate.rate(), 1.0);
261 assert_eq!(rate.weight(), 1);
262 assert_eq!(rate.raw_weight(), 1.0);
263 }
264}