smart-tree 8.0.1

Smart Tree - An intelligent, AI-friendly directory visualization tool
Documentation
//! In-Memory Logger for Smart Tree
//!
//! A custom tracing layer that captures log events and stores them in a
//! thread-safe, capped in-memory buffer. This allows the daemon to expose
//! recent logs via an API endpoint.

use serde::Serialize;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use tracing::Subscriber;
use tracing_subscriber::Layer;

const MAX_LOG_ENTRIES: usize = 1000;

#[derive(Debug, Clone, Serialize)]
pub struct LogEntry {
    pub timestamp: String,
    pub level: String,
    pub message: String,
}

#[derive(Clone, Debug)]
pub struct InMemoryLogStore {
    pub entries: Arc<Mutex<VecDeque<LogEntry>>>,
}

impl Default for InMemoryLogStore {
    fn default() -> Self {
        Self::new()
    }
}

impl InMemoryLogStore {
    pub fn new() -> Self {
        Self {
            entries: Arc::new(Mutex::new(VecDeque::with_capacity(MAX_LOG_ENTRIES))),
        }
    }
}

pub struct InMemoryLoggerLayer {
    store: InMemoryLogStore,
}

impl InMemoryLoggerLayer {
    pub fn new(store: InMemoryLogStore) -> Self {
        Self { store }
    }
}

// A visitor to format the log message.
struct LogVisitor {
    message: String,
}

impl tracing::field::Visit for LogVisitor {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        if field.name() == "message" {
            self.message = format!("{:?}", value);
        }
    }
}

impl<S> Layer<S> for InMemoryLoggerLayer
where
    S: Subscriber,
{
    fn on_event(
        &self,
        event: &tracing::Event<'_>,
        _ctx: tracing_subscriber::layer::Context<'_, S>,
    ) {
        let mut visitor = LogVisitor {
            message: String::new(),
        };
        event.record(&mut visitor);

        let metadata = event.metadata();
        let entry = LogEntry {
            timestamp: chrono::Utc::now().to_rfc3339(),
            level: metadata.level().to_string(),
            message: visitor.message,
        };

        if let Ok(mut entries) = self.store.entries.lock() {
            if entries.len() >= MAX_LOG_ENTRIES {
                entries.pop_front();
            }
            entries.push_back(entry);
        }
    }
}