metrics/
metadata.rs

1/// Verbosity of a metric.
2#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
3pub struct Level(LevelInner);
4
5impl Level {
6    /// The "trace" level.
7    pub const TRACE: Self = Self(LevelInner::Trace);
8
9    /// The "debug" level.
10    pub const DEBUG: Self = Self(LevelInner::Debug);
11
12    /// The "info" level.
13    pub const INFO: Self = Self(LevelInner::Info);
14
15    /// The "warn" level.
16    pub const WARN: Self = Self(LevelInner::Warn);
17
18    /// The "error" level.
19    pub const ERROR: Self = Self(LevelInner::Error);
20}
21
22impl std::convert::TryFrom<&str> for Level {
23    type Error = String;
24
25    fn try_from(value: &str) -> Result<Self, Self::Error> {
26        match value.trim() {
27            "trace" | "TRACE" => Ok(Level::TRACE),
28            "debug" | "DEBUG" => Ok(Level::DEBUG),
29            "info" | "INFO" => Ok(Level::INFO),
30            "warn" | "WARN" => Ok(Level::WARN),
31            "error" | "ERROR" => Ok(Level::ERROR),
32            unknown => Err(format!("unknown log level: {} (expected one of 'trace', 'debug', 'info', 'warn', or 'error')", unknown)),
33        }
34    }
35}
36
37#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
38enum LevelInner {
39    Trace = 0,
40    Debug = 1,
41    Info = 2,
42    Warn = 3,
43    Error = 4,
44}
45
46/// Metadata describing a metric.
47///
48/// All metrics have the following metadata:
49///
50/// - A [`target`](Metadata::target), a string that categorizes part of the system where metric originates from. The
51///   `metrics`` macros default to using the module path where the metric originate as the target, but it may be
52///   overridden.
53/// - A [`level`](Metadata::level), specifying the verbosity the metric is emitted at.
54///
55/// In addition, the following optional metadata describing the source code location where the metric originated from
56/// may be provided:
57///
58/// - The [module path](Metadata::module_path) of the source code location where the metric event originated.
59///
60/// Metadata usage is exporter-specific, and may be ignored entirely. See the documentation of the specific exporter
61/// being used for more information.
62#[derive(Clone, Debug, Eq, PartialEq)]
63pub struct Metadata<'a> {
64    target: &'a str,
65    level: Level,
66    module_path: Option<&'a str>,
67}
68
69impl<'a> Metadata<'a> {
70    /// Constructs a new [`Metadata`].
71    pub const fn new(target: &'a str, level: Level, module_path: Option<&'a str>) -> Self {
72        Self { target, level, module_path }
73    }
74
75    /// Returns the verbosity level of the metric.
76    pub fn level(&self) -> &Level {
77        &self.level
78    }
79
80    /// Returns the target of the metric.
81    ///
82    /// This specifies the part of the system where the metric originates from. Typically, this is the module path where
83    /// the metric originated from, but can be overridden when registering a metric.
84    pub fn target(&self) -> &'a str {
85        self.target
86    }
87
88    /// Returns the module path of the metric.
89    ///
90    /// This specifies the module where the metric originates from, or `None` if the module path is unknown.
91    pub fn module_path(&self) -> Option<&'a str> {
92        self.module_path
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use std::convert::TryFrom as _;
99
100    use super::*;
101
102    #[test]
103    fn level_try_from_valid() {
104        let cases = &[
105            ("trace", Level::TRACE),
106            ("TRACE", Level::TRACE),
107            ("debug", Level::DEBUG),
108            ("DEBUG", Level::DEBUG),
109            ("info", Level::INFO),
110            ("INFO", Level::INFO),
111            ("warn", Level::WARN),
112            ("WARN", Level::WARN),
113            ("error", Level::ERROR),
114            ("ERROR", Level::ERROR),
115        ];
116
117        for (input, expected) in cases {
118            assert_eq!(Level::try_from(*input).unwrap(), *expected);
119
120            // Now try with some whitespace on either end.
121            let input_whitespace = format!("  {}  ", input);
122            assert_eq!(Level::try_from(&*input_whitespace).unwrap(), *expected);
123        }
124    }
125
126    #[test]
127    fn level_try_from_invalid() {
128        let cases = &["", "foo", "bar", "baz", "qux", "quux"];
129
130        for input in cases {
131            assert!(Level::try_from(*input).is_err());
132        }
133    }
134
135    #[test]
136    fn level_ordering() {
137        // A few manual comparisons because it makes me feel better:
138        assert!(Level::TRACE < Level::DEBUG);
139        assert!(Level::DEBUG < Level::INFO);
140        assert!(Level::ERROR > Level::DEBUG);
141        assert!(Level::WARN == Level::WARN);
142
143        // Now check each level programmatically.
144        let levels = &[Level::TRACE, Level::DEBUG, Level::INFO, Level::WARN, Level::ERROR];
145
146        for i in 0..levels.len() {
147            let current_level = levels[i];
148            let lower_levels = &levels[..i];
149            let higher_levels = &levels[i + 1..];
150
151            for lower_level in lower_levels {
152                assert!(current_level > *lower_level);
153                assert!(*lower_level < current_level);
154            }
155
156            for higher_level in higher_levels {
157                assert!(current_level < *higher_level);
158                assert!(*higher_level > current_level);
159            }
160        }
161    }
162}