Skip to main content

log_analyzer/stores/
log_store.rs

1use log_source::source::log_source::LogSource;
2use parking_lot::RwLock;
3use rustc_hash::FxHashMap as HashMap;
4use std::{iter::Iterator, ops::Range, sync::Arc};
5
6/// Store holding raw information
7///
8/// Manages raw lines and associated format
9pub trait LogStore {
10    /// Add a new log to the store
11    fn add_log(
12        &self,
13        log_id: &str,
14        log_source: Arc<Box<dyn LogSource + Send + Sync>>,
15        format: Option<&String>,
16        enabled: bool,
17    );
18    /// Add a single line to the given log id
19    fn add_line(&self, log_id: &str, line: &str);
20    /// Add a many lines to the given log id
21    fn add_lines(&self, log_id: &str, lines: &[String]) -> Range<usize>;
22    /// Get the format associated to the given log id
23    fn get_format(&self, log_id: &str) -> Option<String>;
24    /// Get a list of (enabled, log_id, format(if any))
25    fn get_logs(&self) -> Vec<(bool, String, Option<String>)>;
26    /// Get the log source associated to the log id
27    fn get_source(&self, id: &str) -> Option<Arc<Box<dyn LogSource + Send + Sync>>>;
28    /// Get a list of all the lines for the requested log. WARNING: clones
29    fn get_lines(&self, log_id: &str) -> Vec<String>;
30    /// Get a list of all the lines for the requested log. WARNING: moves
31    fn extract_lines(&self, log_id: &str) -> Vec<String>;
32    /// Get the count of all the lines
33    fn get_total_lines(&self) -> usize;
34    /// Change the enabled state of the given log
35    fn toggle_log(&self, log_id: &str);
36}
37
38pub struct InMemmoryLogStore {
39    /// K: log_path -> V: lines
40    raw_lines: RwLock<Vec<(String, Vec<String>)>>,
41    /// K: log_path -> V: format
42    format: RwLock<HashMap<String, String>>,
43    /// K: log_path -> V: enabled
44    enabled: RwLock<HashMap<String, bool>>,
45    /// K: log_path -> V: source controller
46    source: RwLock<HashMap<String, Arc<Box<dyn LogSource + Send + Sync>>>>,
47}
48
49impl InMemmoryLogStore {
50    pub fn new() -> Self {
51        Self {
52            raw_lines: RwLock::new(Vec::default()),
53            format: RwLock::new(HashMap::default()),
54            enabled: RwLock::new(HashMap::default()),
55            source: RwLock::new(HashMap::default()),
56        }
57    }
58}
59
60impl Default for InMemmoryLogStore {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl LogStore for InMemmoryLogStore {
67    fn add_log(
68        &self,
69        log_id: &str,
70        log_source: Arc<Box<dyn LogSource + Send + Sync>>,
71        format: Option<&String>,
72        enabled: bool,
73    ) {
74        let (mut source_lock, mut format_lock, mut enabled_lock) = (
75            self.source.write(),
76            self.format.write(),
77            self.enabled.write(),
78        );
79
80        source_lock.insert(log_id.to_string(), log_source);
81        enabled_lock.insert(log_id.to_string(), enabled);
82
83        if let Some(format) = format {
84            format_lock.insert(log_id.to_string(), format.clone());
85        }
86    }
87
88    fn add_line(&self, log_id: &str, line: &str) {
89        let mut raw_lines_lock = self.raw_lines.write();
90
91        if !raw_lines_lock.iter().any(|(id, _)| log_id == id) {
92            raw_lines_lock.push((log_id.to_string(), Vec::new()));
93        }
94        let raw_lines = raw_lines_lock.iter_mut().find(|(id, _)| log_id == id).unwrap();
95        raw_lines.1.push(line.to_string());
96    }
97
98    fn add_lines(&self, log_id: &str, lines: &[String]) -> Range<usize> {
99        let mut raw_lines_lock = self.raw_lines.write();
100
101        if !raw_lines_lock.iter().any(|(id, _)| log_id == id) {
102            raw_lines_lock.push((log_id.to_string(), Vec::new()));
103        }
104        let (_, raw_lines) = raw_lines_lock.iter_mut().find(|(id, _)| log_id == id).unwrap();
105        let current_len = raw_lines.len();
106        raw_lines.append(&mut lines.to_vec());
107
108        let new_len = raw_lines.len();
109        current_len..new_len
110    }
111
112    fn get_lines(&self, log_id: &str) -> Vec<String> {
113        match self.raw_lines.read().iter().find(|(id, _)| log_id == id) {
114            Some((_, lines)) => lines.clone(),
115            _ => Vec::new(),
116        }
117    }
118
119    fn extract_lines(&self, log_id: &str) -> Vec<String> {
120        let mut w = self.raw_lines.write();
121        let (_, lines) = std::mem::take(w.iter_mut().find(|(id, _)| log_id == id).unwrap());
122
123        lines
124    }
125
126    fn get_logs(&self) -> Vec<(bool, String, Option<String>)> {
127        let (format_lock, enabled_lock) = (self.format.read(), self.enabled.read());
128
129        let logs: Vec<(bool, String, Option<String>)> = enabled_lock
130            .iter()
131            .map(|(path, enabled)| (*enabled, path.clone(), format_lock.get(path).cloned()))
132            .collect();
133        logs
134    }
135
136    fn get_format(&self, log_id: &str) -> Option<String> {
137        let format_lock = self.format.read();
138        format_lock.get(log_id).cloned()
139    }
140
141    fn get_total_lines(&self) -> usize {
142        self.raw_lines
143            .read()
144            .iter()
145            .fold(0, |acc, (_, v)| acc + v.len())
146    }
147
148    fn get_source(&self, id: &str) -> Option<Arc<Box<dyn LogSource + Send + Sync>>> {
149        if let Some((_id, source)) = self
150            .source
151            .read()
152            .iter()
153            .find(|(log_id, _source)| *id == **log_id)
154        {
155            Some(source.clone())
156        } else {
157            None
158        }
159    }
160
161    fn toggle_log(&self, log_id: &str) {
162        if let Some(e) = self.enabled.write().get_mut(log_id) {
163            *e = !*e;
164        }
165    }
166}