egui_tracing 0.3.0

Integrates tracing and logging with egui for event collection/visualization
Documentation
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};

use imbl::Vector;
use tracing::{Event, Level, Subscriber};
#[cfg(feature = "log")]
use tracing_log::NormalizeEvent;
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;

use super::event::CollectedEvent;

#[derive(Clone, Debug)]
pub enum AllowedTargets {
    All,
    Selected(Vec<String>),
}

#[derive(Debug, Clone)]
pub struct EventCollector {
    allowed_targets: AllowedTargets,
    max_level: Level,
    max_events: Option<usize>,
    events: Arc<Mutex<Vector<CollectedEvent>>>,
    pending: Arc<Mutex<Vec<CollectedEvent>>>,
    generation: Arc<AtomicU64>,
}

impl EventCollector {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn with_max_level(self, max_level: Level) -> Self {
        Self { max_level, ..self }
    }

    pub fn with_max_events(self, max_events: Option<usize>) -> Self {
        Self {
            max_events,
            ..self
        }
    }

    pub fn allowed_targets(self, allowed_targets: AllowedTargets) -> Self {
        Self {
            allowed_targets,
            ..self
        }
    }

    pub fn max_level(&self) -> Level {
        self.max_level
    }

    pub fn generation(&self) -> u64 {
        self.generation.load(Ordering::Relaxed)
    }

    pub fn len(&self) -> usize {
        self.events.lock().unwrap().len()
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn events(&self) -> Vector<CollectedEvent> {
        self.drain_pending();
        self.events.lock().unwrap().clone()
    }

    pub fn clear(&self) {
        {
            let mut pending = self.pending.lock().unwrap();
            pending.clear();
        }
        let mut events = self.events.lock().unwrap();
        *events = Vector::new();
        self.generation.fetch_add(1, Ordering::Relaxed);
    }

    /// Flush pending events into the main vector, bumping generation once.
    pub fn drain_pending(&self) {
        let mut pending = self.pending.lock().unwrap();
        if pending.is_empty() {
            return;
        }
        let mut events = self.events.lock().unwrap();
        for event in pending.drain(..) {
            events.push_back(event);
        }
        if let Some(max) = self.max_events {
            while events.len() > max {
                events.pop_front();
            }
        }
        self.generation.fetch_add(1, Ordering::Relaxed);
    }

    fn collect(&self, event: CollectedEvent) {
        if event.level <= self.max_level {
            let should_collect = match self.allowed_targets {
                AllowedTargets::All => true,
                AllowedTargets::Selected(ref selection) => selection
                    .iter()
                    .any(|target| event.target.starts_with(target)),
            };
            if should_collect {
                self.pending.lock().unwrap().push(event);
            }
        }
    }
}

impl Default for EventCollector {
    fn default() -> Self {
        Self {
            allowed_targets: AllowedTargets::All,
            max_events: None,
            events: Arc::new(Mutex::new(Vector::new())),
            pending: Arc::new(Mutex::new(Vec::new())),
            generation: Arc::new(AtomicU64::new(0)),
            max_level: Level::DEBUG,
        }
    }
}

impl<S> Layer<S> for EventCollector
where
    S: Subscriber + for<'a> LookupSpan<'a>,
{
    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
        #[cfg(feature = "log")]
        let normalized_meta = event.normalized_metadata();
        #[cfg(feature = "log")]
        let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
        #[cfg(not(feature = "log"))]
        let meta = event.metadata();

        self.collect(CollectedEvent::new(event, meta));
    }
}