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(
283 &mut self,
284 regex_str: &str,
285 level: crate::log::Level,
286 ) -> Result<(), TrackConfigError> {
287 match Regex::new(regex_str) {
288 Ok(regex) => self.regex_to_entity_level.push((regex, level)),
289 Err(e) => {
290 return Err(TrackConfigError(format!(
291 "Failed to parse regex {regex_str}:\n{e}\n"
292 )));
293 }
294 }
295 Ok(())
296 }
297
298 pub fn set_monitor_window_size_for(
309 &mut self,
310 regex_str: &str,
311 window_size_ticks: u64,
312 ) -> Result<(), TrackConfigError> {
313 match Regex::new(regex_str) {
314 Ok(regex) => self
315 .regex_to_enable_monitors_for
316 .push((regex, window_size_ticks)),
317 Err(e) => {
318 return Err(TrackConfigError(format!(
319 "Failed to parse regex {regex_str}:\n{e}\n"
320 )));
321 }
322 }
323 Ok(())
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use log::Level;
330
331 use super::*;
332
333 fn entity_paths() -> Vec<&'static str> {
334 vec!["top", "top::dev", "top::dev::node0", "top::dev::node1"]
335 }
336
337 #[test]
338 fn no_filters() {
339 let manager = EntityManager::new(Level::Error);
340
341 for p in entity_paths() {
342 assert_eq!(manager.log_level_for(p, None), Level::Error);
343 }
344 }
345
346 #[test]
347 fn filter_dev_trace() {
348 let mut manager = EntityManager::new(Level::Error);
349 manager
350 .add_entity_level_filter(r".*dev.*", Level::Trace)
351 .unwrap();
352
353 let expected_levels = [Level::Error, Level::Trace, Level::Trace, Level::Trace];
354
355 for (i, p) in entity_paths().iter().enumerate() {
356 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
357 }
358 }
359
360 #[test]
361 fn filter_node0_error() {
362 let mut manager = EntityManager::new(Level::Warn);
363 manager
364 .add_entity_level_filter(r".*node0", Level::Error)
365 .unwrap();
366
367 let expected_levels = [Level::Warn, Level::Warn, Level::Error, Level::Warn];
368
369 for (i, p) in entity_paths().iter().enumerate() {
370 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
371 }
372 }
373
374 #[test]
375 fn filter_node0_warn() {
376 let mut manager = EntityManager::new(Level::Error);
377 manager
378 .add_entity_level_filter(r".*node0", Level::Warn)
379 .unwrap();
380
381 let expected_levels = [Level::Error, Level::Error, Level::Warn, Level::Error];
382
383 for (i, p) in entity_paths().iter().enumerate() {
384 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
385 }
386 }
387
388 #[test]
389 fn filter_dev_and_node0_info() {
390 let mut manager = EntityManager::new(Level::Error);
391 manager
393 .add_entity_level_filter(r".*node0", Level::Warn)
394 .unwrap();
395 manager
396 .add_entity_level_filter(r".*dev.*", Level::Info)
397 .unwrap();
398
399 let expected_levels = [Level::Error, Level::Info, Level::Warn, Level::Info];
400
401 for (i, p) in entity_paths().iter().enumerate() {
402 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
403 }
404 }
405
406 #[test]
407 fn filter_log_dev_and_node0_info() {
408 let mut manager = EntityManager::new(Level::Error);
409 manager
411 .add_entity_level_filter(r".*node0", Level::Info)
412 .unwrap();
413 manager
414 .add_entity_level_filter(r".*dev.*", Level::Trace)
415 .unwrap();
416 manager
417 .add_entity_level_filter(r"top.*", Level::Warn)
418 .unwrap();
419
420 let expected_levels = [Level::Warn, Level::Trace, Level::Info, Level::Trace];
421
422 for (i, p) in entity_paths().iter().enumerate() {
423 assert_eq!(manager.log_level_for(p, None), expected_levels[i]);
424 }
425 }
426
427 #[test]
428 fn ids() {
429 let manager = EntityManager::new(Level::Error);
430 for i in 0..10 {
431 assert_eq!(manager.unique_id(), Id(i + ROOT.0 + 1));
432 }
433 }
434}