logged_stream/
logger.rs

1use crate::record::Record;
2use crate::RecordKind;
3use std::collections;
4use std::io::Write;
5use std::str::FromStr;
6use std::sync::mpsc;
7
8//////////////////////////////////////////////////////////////////////////////////////////////////////////////
9// Trait
10//////////////////////////////////////////////////////////////////////////////////////////////////////////////
11
12/// Trait for processing log records in [`LoggedStream`].
13///
14/// This trait allows processing log records ([`Record`]) using the [`log`] method. It should be implemented for
15/// structures intended to be used as the logging component within [`LoggedStream`]. The [`log`] method is called
16/// by [`LoggedStream`] for further log record processing (e.g., writing to the console, memory, or database)
17/// after the log record message has been formatted by an implementation of [`BufferFormatter`] and filtered
18/// by an implementation of [`RecordFilter`].
19///
20/// [`log`]: Logger::log
21/// [`LoggedStream`]: crate::LoggedStream
22/// [`RecordFilter`]: crate::RecordFilter
23/// [`BufferFormatter`]: crate::BufferFormatter
24pub trait Logger: Send + 'static {
25    fn log(&mut self, record: Record);
26}
27
28impl Logger for Box<dyn Logger> {
29    fn log(&mut self, record: Record) {
30        (**self).log(record)
31    }
32}
33
34//////////////////////////////////////////////////////////////////////////////////////////////////////////////
35// ConsoleLogger
36//////////////////////////////////////////////////////////////////////////////////////////////////////////////
37
38/// Logger implementation that writes log records to the console.
39///
40/// This implementation of the [`Logger`] trait writes log records ([`Record`]) to the console using the provided
41/// [`log::Level`]. Log records with the [`Error`] kind ignore the provided [`log::Level`] and are always written
42/// with [`log::Level::Error`].
43///
44/// [`Error`]: crate::RecordKind::Error
45#[derive(Debug, Clone)]
46pub struct ConsoleLogger {
47    level: log::Level,
48}
49
50impl ConsoleLogger {
51    /// Construct a new instance of [`ConsoleLogger`] using provided log level [`str`]. Returns an [`Err`] in
52    /// case if provided log level [`str`] was incorrect.
53    pub fn new(level: &str) -> Result<Self, log::ParseLevelError> {
54        let level = log::Level::from_str(level)?;
55        Ok(Self { level })
56    }
57
58    /// Construct a new instance of [`ConsoleLogger`] using provided log level [`str`]. Panics in case if
59    /// provided log level [`str`] was incorrect.
60    pub fn new_unchecked(level: &str) -> Self {
61        Self::new(level).unwrap()
62    }
63}
64
65impl Logger for ConsoleLogger {
66    fn log(&mut self, record: Record) {
67        let level = match record.kind {
68            RecordKind::Error => log::Level::Error,
69            _ => self.level,
70        };
71        log::log!(level, "{} {}", record.kind, record.message)
72    }
73}
74
75impl Logger for Box<ConsoleLogger> {
76    fn log(&mut self, record: Record) {
77        (**self).log(record)
78    }
79}
80
81//////////////////////////////////////////////////////////////////////////////////////////////////////////////
82// MemoryStorageLogger
83//////////////////////////////////////////////////////////////////////////////////////////////////////////////
84
85/// Logger implementation that writes log records to an inner [`VecDeque`] collection.
86///
87/// This implementation of the [`Logger`] trait writes log records ([`Record`]) into an inner collection
88/// ([`collections::VecDeque`]). The length of the inner collection is limited by a number provided during
89/// structure construction. You can retrieve accumulated log records from the inner collection using the
90/// [`get_log_records`] method and clear the inner collection using the [`clear_log_records`] method.
91///
92/// [`VecDeque`]: collections::VecDeque
93/// [`get_log_records`]: MemoryStorageLogger::get_log_records
94/// [`clear_log_records`]: MemoryStorageLogger::clear_log_records
95#[derive(Debug, Clone)]
96pub struct MemoryStorageLogger {
97    storage: collections::VecDeque<Record>,
98    max_length: usize,
99}
100
101impl MemoryStorageLogger {
102    /// Construct a new instance of [`MemoryStorageLogger`] using provided inner collection max length number,
103    pub fn new(max_length: usize) -> Self {
104        Self {
105            storage: collections::VecDeque::new(),
106            max_length,
107        }
108    }
109
110    /// Retrieve log records from inner collection.
111    #[inline]
112    pub fn get_log_records(&self) -> collections::VecDeque<Record> {
113        self.storage.clone()
114    }
115
116    /// Clear inner collection of log records.
117    #[inline]
118    pub fn clear_log_records(&mut self) {
119        self.storage.clear()
120    }
121}
122
123impl Logger for MemoryStorageLogger {
124    fn log(&mut self, record: Record) {
125        self.storage.push_back(record);
126        if self.storage.len() > self.max_length {
127            let _ = self.storage.pop_front();
128        }
129    }
130}
131
132impl Logger for Box<MemoryStorageLogger> {
133    fn log(&mut self, record: Record) {
134        (**self).log(record)
135    }
136}
137
138//////////////////////////////////////////////////////////////////////////////////////////////////////////////
139// ChannelLogger
140//////////////////////////////////////////////////////////////////////////////////////////////////////////////
141
142/// Logger implementation that sends log records via an asynchronous channel.
143///
144/// This implementation of the [`Logger`] trait sends log records ([`Record`]) using the sending-half of an underlying
145/// asynchronous channel. You can obtain the receiving-half of the channel using the [`take_receiver`] and
146/// [`take_receiver_unchecked`] methods.
147///
148/// [`take_receiver`]: ChannelLogger::take_receiver
149/// [`take_receiver_unchecked`]: ChannelLogger::take_receiver_unchecked
150#[derive(Debug)]
151pub struct ChannelLogger {
152    sender: mpsc::Sender<Record>,
153    receiver: Option<mpsc::Receiver<Record>>,
154}
155
156impl ChannelLogger {
157    /// Construct a new instance of [`ChannelLogger`].
158    pub fn new() -> Self {
159        let (sender, receiver) = mpsc::channel();
160        Self {
161            sender,
162            receiver: Some(receiver),
163        }
164    }
165
166    /// Take channel receiving-half. Returns [`None`] if it was already taken.
167    #[inline]
168    pub fn take_receiver(&mut self) -> Option<mpsc::Receiver<Record>> {
169        self.receiver.take()
170    }
171
172    /// Take channel receiving-half. Panics if it was already taken.
173    pub fn take_receiver_unchecked(&mut self) -> mpsc::Receiver<Record> {
174        self.take_receiver().unwrap()
175    }
176}
177
178impl Default for ChannelLogger {
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184impl Logger for ChannelLogger {
185    fn log(&mut self, record: Record) {
186        let _ = self.sender.send(record);
187    }
188}
189
190impl Logger for Box<ChannelLogger> {
191    fn log(&mut self, record: Record) {
192        (**self).log(record)
193    }
194}
195
196//////////////////////////////////////////////////////////////////////////////////////////////////////////////
197// FileLogger
198//////////////////////////////////////////////////////////////////////////////////////////////////////////////
199
200/// This implementation of [`Logger`] trait writes log records ([`Record`]) into provided file.
201pub struct FileLogger {
202    file: std::fs::File,
203}
204
205impl FileLogger {
206    /// Construct a new instance of [`FileLogger`] using provided file.
207    pub fn new(file: std::fs::File) -> Self {
208        Self { file }
209    }
210}
211
212impl Logger for FileLogger {
213    fn log(&mut self, record: Record) {
214        let _ = writeln!(
215            self.file,
216            "[{}] {} {}",
217            record.time.format("%+"),
218            record.kind,
219            record.message
220        );
221    }
222}
223
224impl Logger for Box<FileLogger> {
225    fn log(&mut self, record: Record) {
226        (**self).log(record)
227    }
228}
229
230//////////////////////////////////////////////////////////////////////////////////////////////////////////////
231// Tests
232//////////////////////////////////////////////////////////////////////////////////////////////////////////////
233
234#[cfg(test)]
235mod tests {
236    use crate::logger::ChannelLogger;
237    use crate::logger::ConsoleLogger;
238    use crate::logger::FileLogger;
239    use crate::logger::Logger;
240    use crate::logger::MemoryStorageLogger;
241    use crate::record::Record;
242    use crate::record::RecordKind;
243
244    fn assert_unpin<T: Unpin>() {}
245
246    #[test]
247    fn test_unpin() {
248        assert_unpin::<ConsoleLogger>();
249        assert_unpin::<ChannelLogger>();
250        assert_unpin::<MemoryStorageLogger>();
251        assert_unpin::<FileLogger>();
252    }
253
254    #[test]
255    fn test_trait_object_safety() {
256        // Assert traint object construct.
257        let mut console: Box<dyn Logger> = Box::new(ConsoleLogger::new_unchecked("debug"));
258        let mut memory: Box<dyn Logger> = Box::new(MemoryStorageLogger::new(100));
259        let mut channel: Box<dyn Logger> = Box::new(ChannelLogger::new());
260
261        let record = Record::new(RecordKind::Open, String::from("test log record"));
262
263        // Assert that trait object methods are dispatchable.
264        console.log(record.clone());
265        memory.log(record.clone());
266        channel.log(record);
267    }
268
269    fn assert_logger<T: Logger>() {}
270
271    #[test]
272    fn test_box() {
273        assert_logger::<Box<dyn Logger>>();
274        assert_logger::<Box<ConsoleLogger>>();
275        assert_logger::<Box<MemoryStorageLogger>>();
276        assert_logger::<Box<ChannelLogger>>();
277        assert_logger::<Box<FileLogger>>();
278    }
279
280    fn assert_send<T: Send>() {}
281
282    #[test]
283    fn test_send() {
284        assert_send::<ConsoleLogger>();
285        assert_send::<MemoryStorageLogger>();
286        assert_send::<ChannelLogger>();
287        assert_send::<FileLogger>();
288
289        assert_send::<Box<dyn Logger>>();
290        assert_send::<Box<ConsoleLogger>>();
291        assert_send::<Box<MemoryStorageLogger>>();
292        assert_send::<Box<ChannelLogger>>();
293        assert_send::<Box<FileLogger>>();
294    }
295}