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