Skip to main content

opentracingrust/span/
log.rs

1use std::collections::HashMap;
2use std::collections::hash_map::Iter;
3
4use std::time::SystemTime;
5
6
7/// Structured logging information to attach to spans.
8///
9/// Each log has a set of vaules idenfied by strings.
10/// Values stored in fields can be of any type convertible to a `LogValue`.
11///
12/// Logs also have an optional timestamp.
13/// If set, the timestamp must be between the start and the end of the span.
14///
15/// # Examples
16///
17/// ```
18/// extern crate opentracingrust;
19///
20/// use std::thread;
21/// use std::time::Duration;
22/// use std::time::SystemTime;
23///
24/// use opentracingrust::Log;
25/// use opentracingrust::tracers::NoopTracer;
26///
27///
28/// fn main() {
29///     let (tracer, _) = NoopTracer::new();
30///     let mut span = tracer.span("example");
31///
32///     let time = SystemTime::now();
33///     thread::sleep(Duration::from_millis(50));
34///
35///     let log = Log::new()
36///         .log("error", false)
37///         .log("event", "some-event")
38///         .log("line", 26)
39///         .at(time);
40///     span.log(log);
41/// }
42/// ```
43#[derive(Debug, Default)]
44pub struct Log {
45    fields: LogFileds,
46    timestamp: Option<SystemTime>,
47}
48
49impl Log {
50    /// Creates an empty structured log.
51    pub fn new() -> Log {
52        Log {
53            fields: LogFileds::new(),
54            timestamp: None,
55        }
56    }
57}
58
59impl Log {
60    /// Sets the timestamp associated with the log.
61    pub fn at(mut self, timestamp: SystemTime) -> Log {
62        self.timestamp = Some(timestamp);
63        self
64    }
65
66    /// Sets the timestamp to now if not set.
67    pub fn at_or_now(&mut self) {
68        if self.timestamp.is_none() {
69            self.timestamp = Some(SystemTime::now())
70        }
71    }
72
73    /// Extend the log fields with the given value.
74    ///
75    /// If a value with the same key is already in the log the value is replaced.
76    pub fn log<LV: Into<LogValue>>(mut self, key: &str, value: LV) -> Log {
77        self.fields.log(key.into(), value.into());
78        self
79    }
80
81    /// Access an iterator over stored fields.
82    pub fn iter(&self) -> Iter<String, LogValue> {
83        self.fields.iter()
84    }
85
86    /// Access the (optional) timestamp for the log.
87    pub fn timestamp(&self) -> Option<&SystemTime> {
88        self.timestamp.as_ref()
89    }
90}
91
92
93/// Structured log fields container.
94#[derive(Debug, Default)]
95struct LogFileds(HashMap<String, LogValue>);
96
97impl LogFileds {
98    /// Creates an empty
99    pub fn new() -> LogFileds {
100        LogFileds(HashMap::new())
101    }
102
103    /// Insert/update a field.
104    pub fn log(&mut self, key: String, value: LogValue) {
105        self.0.insert(key, value);
106    }
107
108    /// Access an iterator over fields.
109    pub fn iter(&self) -> Iter<String, LogValue> {
110        self.0.iter()
111    }
112}
113
114
115/// Enumeration of valid types for log values.
116#[derive(Debug, PartialEq)]
117pub enum LogValue {
118    Boolean(bool),
119    Float(f64),
120    Integer(i64),
121    String(String),
122}
123
124impl From<bool> for LogValue {
125    fn from(value: bool) -> LogValue {
126        LogValue::Boolean(value)
127    }
128}
129
130impl From<f64> for LogValue {
131    fn from(value: f64) -> LogValue {
132        LogValue::Float(value)
133    }
134}
135
136impl From<i64> for LogValue {
137    fn from(value: i64) -> LogValue {
138        LogValue::Integer(value)
139    }
140}
141
142impl<'a> From<&'a str> for LogValue {
143    fn from(value: &'a str) -> LogValue {
144        LogValue::String(String::from(value))
145    }
146}
147
148impl From<String> for LogValue {
149    fn from(value: String) -> LogValue {
150        LogValue::String(value)
151    }
152}
153
154
155#[cfg(test)]
156mod tests {
157    use std::time::Duration;
158    use std::time::SystemTime;
159
160    use super::Log;
161    use super::LogValue;
162
163    #[test]
164    fn add_field() {
165        let log = Log::new().log("key", "value");
166        let entries: Vec<(&String, &LogValue)> = log.iter().collect();
167        assert_eq!(entries, [
168            (&String::from("key"), &LogValue::String(String::from("value")))
169        ]);
170    }
171
172    #[test]
173    fn defults_to_no_time() {
174        match Log::new().timestamp() {
175            None => (),
176            _ => panic!("Time should not be set")
177        }
178    }
179
180    #[test]
181    fn set_default_timestamp() {
182        let start = SystemTime::now();
183        let mut log = Log::new();
184        log.at_or_now();
185        let time = log.timestamp().unwrap();
186        let duration = time.duration_since(start).unwrap();
187        if duration > Duration::from_millis(100) {
188            panic!("Log timestamp too far from expected time");
189        }
190    }
191
192    #[test]
193    fn set_log_timestamp() {
194        let time = SystemTime::now();
195        let log = Log::new().at(time.clone());
196        assert_eq!(&time, log.timestamp().unwrap());
197    }
198}