Skip to main content

aya_metrics_common/
lib.rs

1// #![no_std] except for tests and user space!
2#![cfg_attr(not(any(test, feature = "user")), no_std)]
3#![deny(missing_docs)]
4#![deny(clippy::unwrap_used)]
5
6//! Provides common metric functionality for use in both user space and in BPF.
7//!
8
9/// The maximum number of counters that can be inserted the BPF per CPU array.
10/// Setting it to 64 is arbitrary and this should be generalized before releasing [aya-metrics-ebpf].
11pub const BPF_COUNTERS_MAX_ENTRIES: usize = 64;
12
13/// The kind of [`Meter`].
14///
15/// Currently only [`MeterKind::Counter`] is supported but other meters could be added.
16pub enum MeterKind {
17    /// Counters monitor monotonically increasing values. Counters may never be reset to a lesser value.
18    Counter,
19}
20
21impl MeterKind {
22    /// The name of the BPF map for this kind of [`Meter`].
23    pub fn map_name(&self) -> &str {
24        match self {
25            MeterKind::Counter => "COUNTERS",
26        }
27    }
28}
29
30/// Seal traits with a supertrait.
31/// A public supertrait whose name is not publicly exported.
32mod private {
33    pub trait Sealed {}
34}
35
36/// A base trait for all kinds of meters.
37///
38/// It is expected to be used on an enum representing a set of meters of the same kind in the same BPF map.
39pub trait Meter: Copy + private::Sealed {
40    /// The kind of meter.
41    fn kind() -> MeterKind;
42
43    /// The index of the meter in a BPF map.
44    fn index(&self) -> u32;
45
46    /// The name of the meter.
47    #[cfg(any(test, feature = "user"))]
48    fn name(self) -> String;
49
50    /// The description of the meter.
51    #[cfg(any(test, feature = "user"))]
52    fn description(self) -> String;
53}
54
55/// A trait which should be implemented over an enumeration defining counters in the same BPF map.
56///
57/// Counters monitor monotonically increasing values and never reset to a lesser value.
58/// Each enumeration should represents an index into a BPF map.
59pub trait Counter: Meter {
60    /// The index of the counter in a BPF map.
61    fn index(&self) -> u32;
62
63    /// The name of the counter.
64    #[cfg(any(test, feature = "user"))]
65    fn name(self) -> String;
66
67    /// The description of the meter.
68    ///
69    /// Implementing this is optional and by default will return an empty string.
70    #[cfg(any(test, feature = "user"))]
71    fn description(self) -> String {
72        String::new()
73    }
74}
75
76impl<T: Counter> private::Sealed for T {}
77
78/// Blanket implementation for all [`Counter`]s.
79impl<T: Counter> Meter for T {
80    fn kind() -> MeterKind {
81        MeterKind::Counter
82    }
83
84    fn index(&self) -> u32 {
85        Counter::index(self)
86    }
87
88    #[cfg(any(test, feature = "user"))]
89    fn name(self) -> String {
90        Counter::name(self)
91    }
92
93    #[cfg(any(test, feature = "user"))]
94    fn description(self) -> String {
95        Counter::description(self)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::{Meter, MeterKind};
102
103    // Create a test enum implementing Counter
104    #[derive(Debug, Copy, Clone)]
105    enum MockCounter {
106        First,
107        Second,
108    }
109
110    impl super::Counter for MockCounter {
111        fn name(self) -> String {
112            match self {
113                MockCounter::First => "first_counter".to_string(),
114                MockCounter::Second => "second_counter".to_string(),
115            }
116        }
117
118        fn index(&self) -> u32 {
119            match self {
120                MockCounter::First => 0,
121                MockCounter::Second => 1,
122            }
123        }
124    }
125
126    #[test]
127    fn test_counter() {
128        let first = MockCounter::First;
129        let second = MockCounter::Second;
130
131        // Test names
132        assert_eq!(first.name(), "first_counter");
133        assert_eq!(second.name(), "second_counter");
134
135        // Test index values
136        assert_eq!(first.index(), 0);
137        assert_eq!(second.index(), 1);
138    }
139
140    #[test]
141    fn test_meter_trait() {
142        let counter = MockCounter::First;
143
144        // Test that kind returns Counter
145        assert!(matches!(MockCounter::kind(), MeterKind::Counter));
146
147        // Test that index matches through Meter trait
148        assert_eq!(<MockCounter as Meter>::index(&counter), 0);
149
150        // Test that name matches through Meter trait
151        assert_eq!(<MockCounter as Meter>::name(counter), "first_counter");
152    }
153
154    #[test]
155    fn test_meter_kind() {
156        assert_eq!(MeterKind::Counter.map_name(), "COUNTERS");
157    }
158}