1pub mod aka;
7pub mod capnp;
9pub mod dev_null;
11#[cfg(feature = "perfetto")]
13pub mod perfetto;
14pub mod text;
16pub mod types;
18
19pub mod multi_tracker;
21
22use std::cell::RefCell;
23use std::collections::HashMap;
24use std::io;
25use std::rc::Rc;
26
27pub use capnp::CapnProtoTracker;
28pub use dev_null::DevNullTracker;
29use regex::Regex;
30pub use text::TextTracker;
31
32use crate::entity::Capacity;
33use crate::tracker::aka::AlternativeNames;
34use crate::{Id, ROOT};
35
36#[derive(Debug)]
38pub struct TrackConfigError(pub String);
39
40pub trait Track {
42 fn unique_id(&self) -> Id;
44
45 fn enabled_level(&self, id: Id) -> log::Level;
47
48 fn is_entity_enabled(&self, id: Id, level: log::Level) -> bool {
50 level <= self.enabled_level(id)
51 }
52
53 fn monitoring_window_size_for(&self, id: Id) -> Option<u64>;
56
57 fn add_entity(
59 &self,
60 id: Id,
61 entity_name: &str,
62 alternative_names: AlternativeNames,
63 ) -> log::Level;
64
65 fn enter(&self, enter_into: Id, enter_obj: Id);
67
68 fn exit(&self, exit_from: Id, exit_obj: Id);
70
71 fn value(&self, id: Id, value: f64);
73
74 fn create_entity(&self, created_by: Id, id: Id, name: &str);
76
77 fn create_monitor(&self, created_by: Id, id: Id, name: &str);
79
80 fn create_lane(&self, created_by: Id, id: Id, name: &str);
82
83 fn create_group(&self, created_by: Id, id: Id, name: &str);
85
86 fn add_to_group(&self, activity: Id, group_id: Id);
88
89 fn remove_from_group(&self, activity: Id, group_id: Id);
91
92 fn begin_activity(&self, activity: Id, lane: Id, name: &str);
94
95 fn end_activity(&self, activity: Id);
97
98 fn create_object(
100 &self,
101 created_by: Id,
102 id: Id,
103 size: usize,
104 units: &str,
105 req_type: u8,
106 details: &str,
107 );
108
109 fn capacity(&self, id: Id, capacity: Capacity);
111
112 fn destroy(&self, destroyed_by: Id, destroyed_obj: Id);
114
115 fn connect(&self, connect_from: Id, connect_to: Id);
117
118 fn log(&self, msg_by: Id, level: log::Level, msg: std::fmt::Arguments);
120
121 fn time(&self, set_by: Id, time_ns: f64);
123
124 fn shutdown(&self);
126}
127
128pub type Tracker = Rc<dyn Track>;
130
131#[must_use]
133pub fn stdout_tracker(level: log::Level) -> Tracker {
134 let entity_manger = EntityManager::new(level);
135 let stdout_writer = Box::new(std::io::BufWriter::new(io::stdout()));
136 let tracker: Tracker = Rc::new(TextTracker::new(entity_manger, stdout_writer));
137 tracker
138}
139
140#[must_use]
142pub fn dev_null_tracker() -> Tracker {
143 let tracer: Tracker = Rc::new(DevNullTracker {});
144 tracer
145}
146
147pub struct EntityManager {
156 default_entity_level: log::Level,
158
159 regex_to_entity_level: Vec<(Regex, log::Level)>,
161
162 regex_to_enable_monitors_for: Vec<(Regex, u64)>,
164
165 unique_id: RefCell<u64>,
167
168 log_entity_lookup: RefCell<HashMap<Id, log::Level>>,
171
172 monitor_window_size_lookup: RefCell<HashMap<Id, u64>>,
174}
175
176impl EntityManager {
177 #[must_use]
179 pub fn new(default_entity_level: log::Level) -> Self {
180 Self {
181 default_entity_level,
182 regex_to_entity_level: Vec::new(),
183 regex_to_enable_monitors_for: Vec::new(),
184 unique_id: RefCell::new(ROOT.0 + 1),
185 log_entity_lookup: RefCell::new(HashMap::new()),
186 monitor_window_size_lookup: RefCell::new(HashMap::new()),
187 }
188 }
189
190 fn unique_id(&self) -> Id {
191 let mut guard = self.unique_id.borrow_mut();
192 let id = *guard;
193 *guard += 1;
194 Id(id)
195 }
196
197 fn enabled_level(&self, id: Id) -> log::Level {
198 match self.log_entity_lookup.borrow().get(&id) {
199 None => self.default_entity_level,
200 Some(entity_level) => *entity_level,
201 }
202 }
203
204 fn monitoring_window_size_for(&self, id: Id) -> Option<u64> {
205 self.monitor_window_size_lookup.borrow().get(&id).copied()
206 }
207
208 fn add_entity(
209 &self,
210 id: Id,
211 entity_name: &str,
212 alternative_names: AlternativeNames,
213 ) -> log::Level {
214 let entity_level = self.log_level_for(entity_name, alternative_names);
215 if entity_level != self.default_entity_level
216 && self
217 .log_entity_lookup
218 .borrow_mut()
219 .insert(id, entity_level)
220 .is_some()
221 {
222 panic!("Entity ID {id} already seen ({entity_name})");
223 }
224
225 if let Some(window_size_ticks) =
226 self.monitor_window_size_for(entity_name, alternative_names)
227 {
228 self.monitor_window_size_lookup
229 .borrow_mut()
230 .insert(id, window_size_ticks);
231 }
232
233 entity_level
234 }
235
236 fn log_level_for(&self, entity_name: &str, alternative_names: AlternativeNames) -> log::Level {
237 for (regex, level) in &self.regex_to_entity_level {
238 if regex.is_match(entity_name) {
239 return *level;
240 }
241 if let Some(alternative_names) = alternative_names {
242 for name in alternative_names {
243 if regex.is_match(name.as_str()) {
244 return *level;
245 }
246 }
247 }
248 }
249 self.default_entity_level
250 }
251
252 fn monitor_window_size_for(
253 &self,
254 entity_name: &str,
255 alternative_names: AlternativeNames,
256 ) -> Option<u64> {
257 for (regex, window_size_ticks) in &self.regex_to_enable_monitors_for {
258 if regex.is_match(entity_name) {
259 return Some(*window_size_ticks);
260 }
261 if let Some(alternative_names) = alternative_names {
262 for name in alternative_names {
263 if regex.is_match(name.as_str()) {
264 return Some(*window_size_ticks);
265 }
266 }
267 }
268 }
269 None
270 }
271
272 pub fn add_entity_level_filter(
285 &mut self,
286 regex_str: &str,
287 level: crate::log::Level,
288 ) -> Result<(), TrackConfigError> {
289 match Regex::new(regex_str) {
290 Ok(regex) => self.regex_to_entity_level.push((regex, level)),
291 Err(e) => {
292 return Err(TrackConfigError(format!(
293 "Failed to parse regex {regex_str}:\n{e}\n"
294 )));
295 }
296 }
297 Ok(())
298 }
299
300 pub fn set_monitor_window_size_for(
313 &mut self,
314 regex_str: &str,
315 window_size_ticks: u64,
316 ) -> Result<(), TrackConfigError> {
317 match Regex::new(regex_str) {
318 Ok(regex) => self
319 .regex_to_enable_monitors_for
320 .push((regex, window_size_ticks)),
321 Err(e) => {
322 return Err(TrackConfigError(format!(
323 "Failed to parse regex {regex_str}:\n{e}\n"
324 )));
325 }
326 }
327 Ok(())
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use log::Level;
334
335 use super::*;
336
337 fn entity_paths() -> Vec<&'static str> {
338 vec!["top", "top::dev", "top::dev::node0", "top::dev::node1"]
339 }
340
341 #[test]
342 fn no_filters() {
343 let manager = EntityManager::new(Level::Error);
344
345 for p in entity_paths() {
346 assert_eq!(manager.log_level_for(p, None), Level::Error);
347 }
348 }
349
350 #[test]
351 fn filter_dev_trace() {
352 let mut manager = EntityManager::new(Level::Error);
353 manager
354 .add_entity_level_filter(r".*dev.*", Level::Trace)
355 .unwrap();
356
357 let expected_levels = [Level::Error, Level::Trace, Level::Trace, Level::Trace];
358
359 for (i, p) in entity_paths().iter().enumerate() {
360 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
361 }
362 }
363
364 #[test]
365 fn filter_node0_error() {
366 let mut manager = EntityManager::new(Level::Warn);
367 manager
368 .add_entity_level_filter(r".*node0", Level::Error)
369 .unwrap();
370
371 let expected_levels = [Level::Warn, Level::Warn, Level::Error, Level::Warn];
372
373 for (i, p) in entity_paths().iter().enumerate() {
374 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
375 }
376 }
377
378 #[test]
379 fn filter_node0_warn() {
380 let mut manager = EntityManager::new(Level::Error);
381 manager
382 .add_entity_level_filter(r".*node0", Level::Warn)
383 .unwrap();
384
385 let expected_levels = [Level::Error, Level::Error, Level::Warn, Level::Error];
386
387 for (i, p) in entity_paths().iter().enumerate() {
388 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
389 }
390 }
391
392 #[test]
393 fn filter_dev_and_node0_info() {
394 let mut manager = EntityManager::new(Level::Error);
395 manager
397 .add_entity_level_filter(r".*node0", Level::Warn)
398 .unwrap();
399 manager
400 .add_entity_level_filter(r".*dev.*", Level::Info)
401 .unwrap();
402
403 let expected_levels = [Level::Error, Level::Info, Level::Warn, Level::Info];
404
405 for (i, p) in entity_paths().iter().enumerate() {
406 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
407 }
408 }
409
410 #[test]
411 fn filter_log_dev_and_node0_info() {
412 let mut manager = EntityManager::new(Level::Error);
413 manager
415 .add_entity_level_filter(r".*node0", Level::Info)
416 .unwrap();
417 manager
418 .add_entity_level_filter(r".*dev.*", Level::Trace)
419 .unwrap();
420 manager
421 .add_entity_level_filter(r"top.*", Level::Warn)
422 .unwrap();
423
424 let expected_levels = [Level::Warn, Level::Trace, Level::Info, Level::Trace];
425
426 for (i, p) in entity_paths().iter().enumerate() {
427 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
428 }
429 }
430
431 #[test]
432 fn ids() {
433 let manager = EntityManager::new(Level::Error);
434 for i in 0..10 {
435 assert_eq!(manager.unique_id(), Id(i + ROOT.0 + 1));
436 }
437 }
438}