use std::{ path::Path, sync::{ Arc, atomic::{AtomicU32, Ordering}, }, }; use gix::progress::DoOrDiscard; use parking_lot::Mutex; use rusqlite::params; use tracing_forest::tree::Tree; use tracing_subscriber::layer::SubscriberExt; type ProgressItem = DoOrDiscard; pub fn override_thread_subscriber( db_path: impl AsRef, progress: Option, reverse_lines: bool, ) -> anyhow::Result<(tracing::subscriber::DefaultGuard, Arc)> { let current_id = Arc::new(AtomicU32::default()); let processor = tracing_forest::Printer::new().formatter(StoreTreeToDb { con: Arc::new(Mutex::new(rusqlite::Connection::open(&db_path)?)), run_id: current_id.clone(), progress: progress.map(Mutex::new), reverse_lines, }); let subscriber = tracing_subscriber::Registry::default().with(tracing_forest::ForestLayer::from(processor)); let guard = tracing::subscriber::set_default(subscriber); Ok((guard, current_id)) } pub struct StoreTreeToDb { con: Arc>, run_id: Arc, progress: Option>, reverse_lines: bool, } impl tracing_forest::printer::Formatter for StoreTreeToDb { type Error = rusqlite::Error; fn fmt(&self, tree: &Tree) -> Result { if let Some((progress, tree)) = self .progress .as_ref() .map(Mutex::lock) .zip(tracing_forest::printer::Pretty.fmt(tree).ok()) { use gix::Progress; if self.reverse_lines { for line in tree.lines().rev() { progress.info(line.into()); } } else { for line in tree.lines() { progress.info(line.into()); } } } // TODO: wait for new release of `tracing-forest` and load the ID from span fields. let json = serde_json::to_string_pretty(&tree).expect("serialization to string always works"); let run_id = self.run_id.load(Ordering::SeqCst); self.con .lock() .execute("UPDATE run SET spans_json = ?1 WHERE id = ?2", params![json, run_id])?; Ok(String::new()) } }