gts_logger/logbackend/
consolelogger.rs

1use crate::error::GtsLoggerError;
2use crate::logbackend::LogBackend;
3use crate::logclient::LogEventTs;
4use core::fmt::Debug;
5use gts_transport::error::GtsTransportError;
6use gts_transport::membackend::memchunk::MemChunkHolder;
7use gts_transport::sync::lfringspsc::{spsc_ring_pair, SpScRingData, SpScRingSender};
8use log::info;
9use std::cell::UnsafeCell;
10use std::sync::Arc;
11use std::sync::Mutex;
12use std::time::Duration;
13
14pub struct ConsoleThreadLogBacked<const RSIZE: usize, T>
15where
16    T: Copy + Send,
17{
18    run_flag: Arc<Mutex<bool>>,
19    join_handle: Option<std::thread::JoinHandle<()>>,
20    log_tx: UnsafeCell<SpScRingSender<RSIZE, T, MemChunkHolder<SpScRingData<RSIZE, T>>>>,
21}
22
23impl<T, const RSIZE: usize> ConsoleThreadLogBacked<RSIZE, LogEventTs<T>>
24where
25    T: Copy + Send + 'static + Debug,
26{
27    pub fn new(core_id: Option<usize>) -> Self {
28        let flag = Arc::new(Mutex::new(false));
29        // let queue = Arc::new(Mutex::new(VecDeque::<T>::new()));
30
31        let flag_clone = flag.clone();
32        // let queue_clone = queue.clone();
33        let (log_tx, mut log_rx) =
34            spsc_ring_pair::<RSIZE, LogEventTs<T>, _>(MemChunkHolder::zeroed());
35
36        let join_handle = Some(std::thread::spawn(move || {
37            if let Some(core_id) = core_id {
38                assert!(core_affinity::set_for_current(core_affinity::CoreId {
39                    id: core_id
40                }));
41            }
42            let mut last_ts = None;
43            while !*flag_clone.lock().unwrap() {
44                match log_rx.try_recv() {
45                    Ok(res) => {
46                        let diff = last_ts.map(|val| res.timestamp - val);
47                        match diff {
48                            None => {
49                                info!("[LOG] @{} (-) {:?}", res.timestamp, res.data);
50                            }
51                            Some(diff) => {
52                                info!("[LOG] @{} (+{} ns) {:?}", res.timestamp, diff, res.data);
53                            }
54                        }
55                        last_ts = Some(res.timestamp);
56                    }
57                    Err(GtsTransportError::WouldBlock) => {}
58                    _ => unreachable!(),
59                }
60                std::thread::sleep(Duration::from_millis(10));
61            }
62        }));
63
64        ConsoleThreadLogBacked {
65            run_flag: flag,
66            join_handle,
67            log_tx: log_tx.into(),
68        }
69    }
70}
71
72impl<T, const RSIZE: usize> Drop for ConsoleThreadLogBacked<RSIZE, T>
73where
74    T: Copy + Send,
75{
76    fn drop(&mut self) {
77        *self.run_flag.lock().unwrap() = true;
78        self.join_handle.take().unwrap().join().unwrap();
79    }
80}
81
82impl<T, const RSIZE: usize> LogBackend<T> for ConsoleThreadLogBacked<RSIZE, T>
83where
84    T: Copy + Send + Debug,
85{
86    fn log(&self, event: T) -> Result<(), GtsLoggerError> {
87        // SAFETY: Self is !Sync, only this function uses log_tx,
88        // no reentrancy in this function.
89        // but need verify reentrancy (by signal e.g.)
90        // anyway refcell doesn't check signal-reentrancy either.
91        let log_tx = unsafe { &mut *self.log_tx.get() };
92        log_tx.send(&event).unwrap();
93        Ok(())
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::logbackend::consolelogger::ConsoleThreadLogBacked;
100    use crate::logclient::LogClient;
101    use arrayvec::ArrayString;
102    use serde::{Deserialize, Serialize};
103
104    #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
105    pub struct LogOneStruct {
106        some_num: u64,
107        some_other_num: u64,
108        some_string: ArrayString<16>,
109    }
110
111    #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
112    pub struct LogTwoStruct {
113        some_string: ArrayString<16>,
114    }
115
116    #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
117    #[serde(tag = "t", content = "c")]
118    pub enum LogEvent {
119        LogOneOne(LogOneStruct),
120        LogTwo(LogTwoStruct),
121    }
122
123    #[test]
124    fn create_logger() {
125        let event = LogEvent::LogOneOne(LogOneStruct {
126            some_num: 5,
127            some_other_num: 7,
128            some_string: ArrayString::from("333").unwrap(),
129        });
130
131        let log_client =
132            LogClient::<_, LogEvent>::new(ConsoleThreadLogBacked::<3000, _>::new(None));
133
134        log_client.log(event).unwrap();
135        log_client.log_same(event).unwrap();
136    }
137}