Skip to main content

blvm_consensus/
profile_log.rs

1//! Non-blocking profile logging for IBD and consensus hot paths.
2//!
3//! Replaces `eprintln!` with channel-based logging so validation never blocks on I/O.
4//! A dedicated background thread drains the channel and writes to stderr.
5//! Note: `tracing` also uses stderr; without a global lock, lines can rarely interleave with
6//! timestamps (`evict_ms=0` + `2026-03-29T...` on one physical line). Log analyzers normalize this;
7//! prefer `analyze_ibd_profile.py` over naive `grep` for `[IBD_VALIDATION]` phase=end splits.
8
9use std::io::Write;
10use std::sync::mpsc;
11use std::sync::OnceLock;
12
13/// Capacity for the profile log channel. When full, new messages are dropped (non-blocking).
14const CHANNEL_CAPACITY: usize = 65_536;
15
16static LOGGER: OnceLock<mpsc::SyncSender<String>> = OnceLock::new();
17
18#[allow(dead_code)]
19pub fn sender() -> Option<&'static mpsc::SyncSender<String>> {
20    Some(LOGGER.get_or_init(|| {
21        let (tx, rx) = mpsc::sync_channel(CHANNEL_CAPACITY);
22        std::thread::Builder::new()
23            .name("profile-log".into())
24            .spawn(move || {
25                for msg in rx {
26                    let _ = writeln!(std::io::stderr(), "{msg}");
27                }
28            })
29            .expect("Failed to spawn profile log thread");
30        tx
31    }))
32}
33
34/// Log a profile message without blocking. Drops the message if the channel is full.
35#[macro_export]
36macro_rules! profile_log {
37    ($($arg:tt)*) => {{
38        #[cfg(feature = "profile")]
39        {
40            if let Some(tx) = $crate::profile_log::sender() {
41                let _ = tx.try_send(format!($($arg)*));
42            }
43        }
44    }};
45}